#ifndef _MATHEMATICS_ALGEBRA_INEQUALITY_
#define _MATHEMATICS_ALGEBRA_INEQUALITY_

#include "math_helper.h"
#include "expression_fraction.h"
#include "..\..\utilities\string_helper.h"
using namespace science::utilities;

#include <iostream>
#include <math.h>

namespace science
{
	namespace mathematics
	{
		namespace algebra
		{
			enum class INEQUALITY_OPERATION
			{
				LESS,
				LESS_EQUAL,
				GREATER,
				GREATER_EQUAL,
				UNKNOWN
			};

			class inequality
			{
			private:
				mutable expression_fraction _left;
				mutable expression_fraction _right;
				mutable expression_fraction _middle;
				mutable INEQUALITY_OPERATION _left_operation;
				mutable INEQUALITY_OPERATION _right_operation;

			public:
				inequality()
				{
					_left_operation = INEQUALITY_OPERATION::UNKNOWN;
					_right_operation = INEQUALITY_OPERATION::UNKNOWN;
				}

				inequality(expression_fraction left, expression_fraction right,
					INEQUALITY_OPERATION left_operation)
				{
					_left = left;
					_middle = right;
					_left_operation = left_operation;
					_right_operation = INEQUALITY_OPERATION::UNKNOWN;
				}

				inequality(expression_fraction left, expression_fraction right, expression_fraction middle,
					INEQUALITY_OPERATION left_operation, INEQUALITY_OPERATION right_operation)
				{
					_left = left;
					_right = right;
					_middle = middle;

					_left_operation = left_operation;
					_right_operation = right_operation;
				}

				inequality(std::string inequality_str)
				{
					unsigned int index = 0;
					*this = parse_inequality(inequality_str, index);
				}

				~inequality()
				{

				}

			public:

				inequality& operator+=(double rhs)
				{
					*this = this->operator+(rhs);
					return *this;
				}

				inequality operator+(double rhs)
				{
					return (const_cast<const inequality*>(this))->operator+(rhs);
				}

				inequality operator+(double rhs) const
				{
					inequality exp;

					_left += term(rhs);
					_middle += term(rhs);
					if (_right.is_valid())
						_right += term(rhs);

					return exp;
				}

				inequality& operator-=(double rhs)
				{
					*this = this->operator-(rhs);
					return *this;
				}

				inequality operator-(double rhs)
				{
					return (const_cast<const inequality*>(this))->operator-(rhs);
				}

				inequality operator-(double rhs) const
				{
					inequality exp;

					_left -= term(rhs);
					_middle -= term(rhs);
					if (_right.is_valid())
						_right -= term(rhs);

					return exp;
				}

			public:
				friend std::ostream &operator<<(std::ostream &output, const inequality &in)
				{
					output << in._left;
					switch (in._left_operation)
					{
					case INEQUALITY_OPERATION::GREATER:
						output << ">"; break;
					case INEQUALITY_OPERATION::GREATER_EQUAL:
						output << ">="; break;
					case INEQUALITY_OPERATION::LESS:
						output << "<"; break;
					case INEQUALITY_OPERATION::LESS_EQUAL:
						output << "<="; break;
					default:
						break;
					}

					output << in._middle;

					if (in._right.is_valid())
					{						
						switch (in._right_operation)
						{
						case INEQUALITY_OPERATION::GREATER:
							output << ">"; break;
						case INEQUALITY_OPERATION::GREATER_EQUAL:
							output << ">="; break;
						case INEQUALITY_OPERATION::LESS:
							output << "<"; break;
						case INEQUALITY_OPERATION::LESS_EQUAL:
							output << "<="; break;
						default:
							break;
						}
						output << in._right;
					}
					return output;
				}

			public:
				static inequality parse_inequality(std::string str, unsigned int& index)
				{
					str = string_helper::remove_white_chars(str);
					inequality exp;
					if (index >= str.size())
						return exp;

					exp._left = expression_fraction::parse_expression_fraction(str, index);
					if (str[index] == '<')
					{
						index++;
						if (str[index] == '=')
						{
							exp._left_operation = INEQUALITY_OPERATION::LESS_EQUAL;
							index++;
						}
						else
							exp._left_operation = INEQUALITY_OPERATION::LESS;						
					}
					else if(str[index] == '>')
					{
						index++;
						if (str[index] == '=')
						{
							index++;
							exp._left_operation = INEQUALITY_OPERATION::GREATER_EQUAL;
						}
						else
							exp._left_operation = INEQUALITY_OPERATION::GREATER;						
					}

					if (index >= str.size())
						throw new std::invalid_argument("Invalid inequality string");
					exp._middle = expression_fraction::parse_expression_fraction(str, index);

					if (str[index] == '<')
					{
						index++;
						if (str[index] == '=')
						{
							exp._right_operation = INEQUALITY_OPERATION::LESS_EQUAL;
							index++;
						}
						else
							exp._right_operation = INEQUALITY_OPERATION::LESS;
					}
					else if (str[index] == '>')
					{
						index++;
						if (str[index] == '=')
						{
							index++;
							exp._right_operation = INEQUALITY_OPERATION::GREATER_EQUAL;
						}
						else
							exp._right_operation = INEQUALITY_OPERATION::GREATER;
					}
					if (index >= str.size())
						return exp;
					exp._right = expression_fraction::parse_expression_fraction(str, index);

					return exp;
				}
			};

			static inequality operator+(double lhs, const inequality& rhs)
			{
				return rhs.operator+(lhs);
			}

			static inequality operator-(double lhs, const inequality& rhs)
			{
				return rhs.operator-(lhs);
			}
		}
	}
}

#endif